﻿using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Tokens;
using Nest;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using XYS.Core.Models;
using XYS.Service.Core.Models;
using XYS.Service.Core.Service;

namespace XYS.Core.Util.Helper;

/// <summary>
/// Token 帮助类
/// </summary>
public static class TokenHelper
{
    /// <summary>
    /// 缓存服务
    /// </summary>
    public static ICache cache { get; set; }

    /// <summary>
    /// 本地缓存
    /// </summary>
    public static IMemoryCache memoryCache { get; set; }

    private static string securityKey;
    /// <summary>
    /// key
    /// </summary>
    public static string SecurityKey
    {
        get => securityKey; set
        {
            securityKey = value;
            if (string.IsNullOrWhiteSpace(value))
            {
                Secret = null;
                return;
            }
            Secret = Encoding.UTF8.GetBytes(SecurityKey);
            validationParameters.ValidIssuer = Lssuer;
            validationParameters.ValidAudience = Audience;
            validationParameters.IssuerSigningKey = new SymmetricSecurityKey(Secret); // 签名秘钥
        }
    }
    /// <summary>
    /// Secret
    /// </summary>
    public static byte[] Secret { get; set; }

    /// <summary>
    /// 颁发机构
    /// </summary>
    public static string Lssuer { get; set; } = "xys";
    /// <summary>
    /// 受众标识
    /// </summary>
    public static string Audience { get; set; } = "audiences";

    /// <summary>
    /// Token失效时间 
    /// </summary>
    public static TimeSpan InvalidTime { get; set; } = TimeSpan.FromMinutes(5);

    /// <summary>
    /// 续签有效期
    /// </summary>
    public static TimeSpan RenewalTokenInvalidTime { get; set; } = TimeSpan.FromHours(2);


    private static readonly JwtSecurityTokenHandler tokenHandler = new();

    // 设置 Token 验证参数
    private static readonly TokenValidationParameters validationParameters = new()
    {
        ValidateIssuer = true,           // 验证颁发机构
        ValidateAudience = true,         // 验证受众
        ValidateIssuerSigningKey = true, // 验证签名秘钥
        ValidIssuer = Lssuer,            // 颁发机构
        ValidAudience = Audience,        // 受众
    };

    /// <summary>
    /// 生成Token时，忽略的属性
    /// </summary>
    public static readonly string[] IgnorePropertieName = new string[]
    {
        nameof(CurrentUserInfo.TrackId),
        nameof(CurrentUserInfo.Email),
        nameof(CurrentUserInfo.Token),
        nameof(CurrentUserInfo.Roles),
        nameof(CurrentUserInfo.Depts),
        nameof(CurrentUserInfo.AccountIdString),
        nameof(CurrentUserInfo.OrgIdString),
        nameof(CurrentUserInfo.Menus),
    };

    /// <summary>
    /// 生成Token
    /// </summary>
    /// <param name="currentUserInfo">当前用户信息</param>
    /// <param name="invalidTime">有效期</param>
    /// <returns>Token</returns>
    public static string GenerateJwtToken(CurrentUserInfo currentUserInfo, TimeSpan? invalidTime = null)
    {
        if (invalidTime == null) invalidTime = InvalidTime;
        if (Secret == null) { throw new Exception("JWT 配置错误"); }
        //兜底，如果没传，则自动生成一个
        if (currentUserInfo.DeviceFingerprint.IsNullOrWhiteSpace()) currentUserInfo.DeviceFingerprint = Guid.NewGuid().ToString();

        var claims = new List<Claim> { new("InvalidTime", DateTime.Now.Add(invalidTime.Value).ToString("yyyy-MM-dd HH:mm:ss.fff")) };

        var properties = currentUserInfo.GetType().GetProperties();
        foreach (var propertie in properties)
        {
            if (IgnorePropertieName.Contains(propertie.Name)) continue;

            var val = propertie.GetValue(currentUserInfo);
            if (val != null)
            {
                claims.Add(new(propertie.Name, val.ToJson()));
            }
        }

        var tokenDescripor = new SecurityTokenDescriptor
        {
            //颁发机构
            Issuer = Lssuer,
            //受众
            Audience = Audience,

            Subject = new(claims),
            Expires = DateTime.Now.Add(invalidTime.Value),//有效期

            //对称秘钥SymmetricSecurityKey
            //签名证书(秘钥，加密算法)SecurityAlgorithms
            SigningCredentials = new(new SymmetricSecurityKey(Secret), SecurityAlgorithms.HmacSha256Signature)
        };

        var token = tokenHandler.CreateToken(tokenDescripor);
        var tokenString = tokenHandler.WriteToken(token);

        return tokenString;
    }


    /// <summary>
    /// 验证token合法性
    /// </summary>
    /// <param name="tokenString"></param>
    /// <returns></returns>
    public static bool ValidateToken(string tokenString)
    {
        if (tokenString.IsNullOrWhiteSpace()) return false;
        string token = IgnoreBearerFroToken(System.Web.HttpUtility.UrlDecode(IgnoreBearerFroToken(tokenString)));
        // 验证 Token
        try { tokenHandler.ValidateToken(token, validationParameters, out var data); }
        catch (Microsoft.IdentityModel.Tokens.SecurityTokenExpiredException ex)
        {
            return ex.Message.StartsWith("IDX10223:");
        }
        catch { return false; }
        return true;
    }


    /// <summary>
    /// 续签 Token
    /// </summary>
    /// <param name="tokenOld"></param>
    /// <param name="activateService"></param>
    /// <returns></returns>
    public static string RenewalToken(string tokenOld, EnumActivateService? activateService = null)
    {
        var currentUserInfo = ReadToken(tokenOld);

        return GenerateJwtToken(new()
        {
            DeviceFingerprint = currentUserInfo.DeviceFingerprint,
            IsFake = currentUserInfo.IsFake,
            AccountId = currentUserInfo.AccountId,
            Phone = currentUserInfo.Phone,
            UserName = currentUserInfo.UserName,
            NickName = currentUserInfo.NickName,
            OrgId = currentUserInfo.OrgId,
            OrgInfo = currentUserInfo.OrgInfo,
            ActivateService = activateService ?? currentUserInfo.ActivateService,
        });
    }

    /// <summary>
    /// 失效 token
    /// </summary>
    /// <param name="cacheService"></param>
    /// <param name="token"></param>
    public static void InvalidityToken(ICache cacheService, string token)
    {
        //获取失效token的key
        string key = CacheKeysHelper.InvalidityToken(token);
        //有效期
        TimeSpan invalidTime;

        //读取token内容
        var jwtSecurityToken = tokenHandler.ReadJwtToken(IgnoreBearerFroToken(token));

        var invalidTimeClaim = jwtSecurityToken.Claims.FirstOrDefault(e => e.Type == "InvalidTime");
        if (invalidTimeClaim != null)
        {
            if (DateTime.TryParse(invalidTimeClaim.Value, out var time))
            {
                time = time.Add(RenewalTokenInvalidTime);
                //如果已经失效了，则不用再进入失效列表了
                if (time < DateTime.Now)
                    return;
                //计算失效时间
                invalidTime = time - DateTime.Now;
            }
            else
            {
                //转换失败时，则全时段失效
                invalidTime = InvalidTime.Add(RenewalTokenInvalidTime);
            }
        }
        else
        {
            invalidTime = InvalidTime.Add(RenewalTokenInvalidTime);
        }

        //设置为失效
        cacheService.Set(key, token, invalidTime);
    }


    /// <summary>
    /// 从上下文中 获取token
    /// </summary>
    /// <param name="context"></param>
    /// <param name="logger"></param>
    /// <returns></returns>
    public static string GetToken(MessageReceivedContext context, ILogger logger = null)
    {
        return GetToken(context.Request, logger);
    }


    static List<string> TokenKeys = new() {
        "Authorization-imitate",
        "Authorized-imitate",
        $"Authorized-{GlobalCoreSetting.EnvironmentName.ToLower()}",
        "Token-imitate",
        "Authorization",
        "Authorized",
        "Token",
        "authorized-imitate",
        "authorization-imitate",
        $"authorized-{GlobalCoreSetting.EnvironmentName.ToLower()}",
        "token-imitate",
        "authorization",
        "authorized",
        "token"
        };

    /// <summary>
    /// 从请求中 获取token
    /// </summary>
    /// <param name="request"></param>
    /// <param name="logger"></param>
    /// <returns></returns>
    public static string GetToken(Microsoft.AspNetCore.Http.HttpRequest request, ILogger logger = null)
    {
        string token = null;
        if (request == null) return token;

        //优先从请求头中获取
        foreach (var tokenKey in TokenKeys)
        {
            token = Convert.ToString(request.Headers[tokenKey]);
            //排除掉 前端 传入 奇怪的token比如'undefined','null'
            if (!token.IsNullOrWhiteSpace() && token.Length < 32) token = "";
            if (ValidateToken(token)) break;
        }

        //从cookie中获取token
        if (token.IsNullOrWhiteSpace())
        {
            Dictionary<string, string> cookiesMap = new();
            if (request.Headers.TryGetValue("Cookie", out var cookieStrings) && cookieStrings.Count > 0)
            {
                foreach (var cookieString in cookieStrings)
                {
                    if (string.IsNullOrWhiteSpace(cookieString) || !cookieString.Contains('=')) continue;

                    var cookieStringArray = cookieString.Split(';');
                    foreach (var item in cookieStringArray)
                    {
                        var array = item.Split('=');
                        if (array.Length == 2)
                        {
                            string key = array[0].Trim().ToLower();
                            string value = array[1].Trim();
                            if (!string.IsNullOrWhiteSpace(key) && !string.IsNullOrWhiteSpace(value)) cookiesMap[key] = value;
                        }
                    }
                }
            }
            foreach (var cookie in request.Cookies)
            {
                string key = cookie.Key.Trim().ToLower();
                string value = cookie.Value.Trim();
                if (!string.IsNullOrWhiteSpace(key) && !string.IsNullOrWhiteSpace(value)) cookiesMap[key] = value;
            }

            foreach (var tokenKey in TokenKeys)
            {
                if (!cookiesMap.ContainsKey(tokenKey)) continue;
                token = cookiesMap[tokenKey];
                //排除掉 前端 传入 奇怪的token比如'undefined','null'
                if (!token.IsNullOrWhiteSpace() && token.Length < 32) token = null;
                if (!ValidateToken(token)) token = null;
            }
        }
        return System.Web.HttpUtility.UrlDecode(token);
    }


    /// <summary>
    /// 读取 token 内容
    /// </summary>
    /// <param name="token"></param>
    /// <returns></returns>
    public static CurrentUserInfo ReadToken(string token)
    {
        //读取Claims
        var claimsMaps = GetClaims(token);
        var currentUserInfo = new CurrentUserInfo();
        var properties = currentUserInfo.GetType().GetProperties().Where(p => p.CanWrite);
        foreach (var propertie in properties)
        {
            if (!propertie.PropertyType.IsInterface && claimsMaps.TryGetValue(propertie.Name, out string value))
            {
                if (!string.IsNullOrWhiteSpace(value))
                    propertie.SetValue(currentUserInfo, value.ToObject(propertie.PropertyType));
            }
        }
        currentUserInfo.Token = token;
        return currentUserInfo;
    }

    /// <summary>
    /// 读取Claims
    /// </summary>
    /// <param name="token"></param>
    /// <returns></returns>
    public static Dictionary<string, string> GetClaims(string token) => tokenHandler.ReadJwtToken(IgnoreBearerFroToken(token)).Claims.Distinct(e => e.Type).ToDictionary(e => e.Type, e => e.Value);
    /// <summary>
    /// bearer  ->  token
    /// </summary>
    /// <param name="bearer"></param>
    /// <returns></returns>
    public static string IgnoreBearerFroToken(string bearer)
    {
        string token = bearer;
        while (token.Trim().StartsWith("Bearer "))
        {
            token = token["Bearer ".Length..];
        }

        return token.Trim();
    }

}
